﻿using System;
using System.Web;
using System.Xml;
using System.Xml.Serialization;
using WeixinSDK.Message;

namespace WeixinSDK
{
    public abstract class WxHandler : IHttpHandler
    {
        protected NLog.Logger _logger;
        protected WxSetting _appSetting;

        public WxHandler(WxSetting appSeting)
        {
            _appSetting = appSeting;
            _logger = NLog.LogManager.GetCurrentClassLogger();
        }

        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            _logger.Info(" [Request URL] " + context.Request.Url.ToString());

            if (context.Request.HttpMethod.ToLower() == "post")
            {
                var stream = context.Request.InputStream;
                byte[] b = new byte[stream.Length];
                stream.Read(b, 0, (int)stream.Length);
                var postStr = System.Text.Encoding.UTF8.GetString(b);

                _logger.Info(" [Request POST] " + postStr);

                if (!string.IsNullOrEmpty(postStr))
                {
                    var msg = WxMsg.ParseReceivedMessage(postStr);

                    try
                    {
                        var respone = ProcessRecMessage_All(msg);
                        _logger.Info(" [Repsone POST] " + respone);
                        context.Response.Write(respone);
                    }
                    catch (Exception ex)
                    {
                        _logger.Info(" [Repsone POST ERROR] " + ex.ToString());
                    }

                    context.Response.End();
                }
            }
            else if (!string.IsNullOrEmpty(context.Request.QueryString["echoStr"]))
            {
                var queryStr = context.Request.QueryString;

                if (WeixinValidate(_appSetting.UserToken, queryStr["signature"], queryStr["timestamp"], queryStr["nonce"]))
                {
                    context.Response.Write(queryStr["echoStr"]);
                    context.Response.End();
                }
            }
        }

        #region 处理接受的消息

        protected virtual string ProcessRecMessage_All(WxMsg msg)
        {
            switch (msg.MsgType)
            {
                case WxMsgType.text:
                    return ProcessRecMessage_Text(msg);
                case WxMsgType.image:
                    return ProcessRecMessage_Image(msg);
                case WxMsgType.voice:
                    return ProcessRecMessage_Voice(msg);
                case WxMsgType.video:
                    return ProcessRecMessage_Video(msg);
                case WxMsgType.location:
                    return ProcessRecMessage_Location(msg);
                case WxMsgType.link:
                    return ProcessRecMessage_Link(msg);
                case WxMsgType.@event:
                    return ProcessRecMessage_Event(msg);
            }

            return BuildResponeXml(msg.FromUserName, msg.ToUserName, "未处理的消息");
        }

        protected virtual string ProcessRecMessage_Text(WxMsg msg)
        {
            return BuildResponeXml(msg.FromUserName, msg.ToUserName,
                "你好:" + msg[WxMsgField.Content]);
        }

        protected virtual string ProcessRecMessage_Image(WxMsg msg)
        {
            return BuildResponeXml(msg.FromUserName, msg.ToUserName,
              new ResImage(msg[WxMsgField.MediaId]));
        }

        protected virtual string ProcessRecMessage_Voice(WxMsg msg)
        {
            return BuildResponeXml(msg.FromUserName, msg.ToUserName,
              new ResVoice(msg[WxMsgField.MediaId]));
        }

        protected virtual string ProcessRecMessage_Video(WxMsg msg)
        {
            return BuildResponeXml(msg.FromUserName, msg.ToUserName, "收到视频消息");
        }

        protected virtual string ProcessRecMessage_Location(WxMsg msg)
        {
            return BuildResponeXml(msg.FromUserName, msg.ToUserName,
               "收到地理位置消息：" + msg[WxMsgField.Label]);
        }

        protected virtual string ProcessRecMessage_Link(WxMsg msg)
        {
            return BuildResponeXml(msg.FromUserName, msg.ToUserName,
              "收到链接消息，消息标题：" + msg[WxMsgField.Title]);
        }

        protected virtual string ProcessRecMessage_Event(WxMsg msg)
        {
            if (WxEventType.subscribe.ToString().Equals(msg[WxMsgField.Event], StringComparison.OrdinalIgnoreCase))
            {
                return BuildResponeXml(msg.FromUserName, msg.ToUserName,
                     "Welcome,感谢您对我们的关注和支持");
            }

            return BuildResponeXml(msg.FromUserName, msg.ToUserName,
                  "收到事件消息，事件：" + msg[WxMsgField.Event]);
        }

        #endregion

        #region 静态方法区块

        readonly static XmlWriterSettings settings;
        readonly static XmlSerializerNamespaces emptyNamepsaces;

        static WxHandler()
        {
            settings = new XmlWriterSettings()
            {
                Indent = true,
                OmitXmlDeclaration = true
            };

            emptyNamepsaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
        }

        /// <summary>
        /// 验证消息真实性
        /// </summary>
        /// <param name="token">开发接口配置的访问令牌</param>
        /// <param name="signature">微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。</param>
        /// <param name="timestamp">时间戳</param>
        /// <param name="nonce">随机数</param>
        /// <returns></returns>
        public static bool WeixinValidate(string token, string signature, string timestamp, string nonce)
        {
            string[] ArrTmp = { token, timestamp, nonce };
            Array.Sort(ArrTmp);
            string tmpStr = string.Concat(ArrTmp);
            tmpStr = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
            return tmpStr.Equals(signature, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// 创建回应消息
        /// </summary>
        /// <param name="toUserName"></param>
        /// <param name="fromUserName"></param>
        /// <param name="respData"></param>
        /// <returns></returns>
        public static string BuildResponeXml(string toUserName, string fromUserName, ResData respData)
        {
            WxMsg msg = new WxMsg();
            msg.ToUserName = toUserName;
            msg.FromUserName = fromUserName;
            msg.MsgType = respData.MsgType;

            if (respData.MsgType == WxMsgType.text)
                msg[WxMsgField.Content] = ((ResText)respData).Content;

            var sb = new System.Text.StringBuilder();

            using (var xr = XmlWriter.Create(sb, settings))
            {
                xr.WriteStartElement("xml");

                //----消息头
                foreach (var kv in msg)
                {
                    xr.WriteStartElement(kv.Key);
                    xr.WriteCData(kv.Value);
                    xr.WriteEndElement();
                }

                var time = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
                xr.WriteElementString(WxMsgField.CreateTime.ToString(), ((int)(DateTime.Now - time).TotalSeconds).ToString());

                //----消息实体内容
                if (respData.MsgType != WxMsgType.text)
                {
                    if (respData.MsgType == WxMsgType.news)
                        xr.WriteElementString(WxMsgField.ArticleCount.ToString(), ((ResNews)respData).ArticleCount.ToString());
                    new XmlSerializer(respData.GetType()).Serialize(xr, respData, emptyNamepsaces);
                }

                xr.WriteEndElement();
            }

            return sb.ToString();
        }

        #endregion
    }
}
